﻿/**************************************************************
 *
 * 唯一标识：0068360b-b7ef-4980-802c-0ac9a4973466
 * 命名空间：Sgr.Caching.Services
 * 创建时间：2023/8/23 7:21:33
 * 机器名称：DESKTOP-S0D075D
 * 创建者：antho
 * 电子邮箱：fengqinhua2016@163.com
 * 描述：内存缓存管理器实现
 *
 *   针对缓存击穿的其它解决方案：
 *
 *   方案一：AsyncLock + 对象池
 *
 *   private readonly ObjectPool<AsyncLock> _lockPool;
 *   private readonly ConcurrentDictionary<string, AsyncLock> _activeLocks;
 *
 *   优点：锁粒度更精确，每个key都有独立的锁；通过对象池减少对象创建开销；理论上并发性能更好
 *   缺点：内存占用会随着缓存key的增加而增加；需要额外的锁生命周期管理；实现复杂度较高
 *
 *   方案二：固定数量锁 + 哈希映射
 *
 *   private readonly SemaphoreSlim[] _stripedLocks;
 *   private const int DEFAULT_LOCK_COUNT = 1024;
 *
 *   优点：内存占用固定且可控；实现简单，维护成本低；无需管理锁的生命周期
 *   缺点：存在哈希冲突的可能；并发性能取决于锁数量的设置
 *
 *   方案三：使用ConcurrentDictionary + SemaphoreSlim
 *
 *   使用的时候创建，使用完成后移除
 *
 **************************************************************/

using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using System.Collections.Concurrent;

namespace Sgr.Caching.Services
{
    /// <summary>
    /// 基于内存的缓存管理器实现
    /// </summary>
    public class MemoryCacheManager : ICacheManager
    {
        private volatile bool _disposed;
        private readonly int _maxLocks;

        // 添加锁定超时配置
        private readonly TimeSpan _lockTimeout;

        private CancellationTokenSource _clearToken = new();

        private readonly ConcurrentDictionary<string, SemaphoreRef> _locks = new();
        private readonly ConcurrentDictionary<string, byte> _keysList = new();
        private readonly CacheOptions _cacheOptions;
        private readonly IMemoryCache _memoryCache;

        private readonly ILogger<MemoryCacheManager> _logger;

        /// <summary>
        /// 初始化内存缓存管理器
        /// </summary>
        /// <param name="options">缓存配置选项</param>
        /// <param name="memoryCache">内存缓存实例</param>
        /// <param name="logger">日志</param>
        /// <exception cref="ArgumentNullException">参数为空时抛出</exception>
        public MemoryCacheManager(IOptions<CacheOptions> options,
            IMemoryCache memoryCache,
            ILogger<MemoryCacheManager> logger)
        {
            _cacheOptions = options?.Value ?? throw new ArgumentNullException(nameof(options));
            _memoryCache = memoryCache ?? throw new ArgumentNullException(nameof(memoryCache));
            _logger = logger;

            // 从配置中获取最大锁数量，默认1024
            _maxLocks = _cacheOptions.StripedLockCount ?? 1024;

            _lockTimeout = TimeSpan.FromMilliseconds(_cacheOptions.LockTimeoutMilliseconds <= 0
                ? 3000
                : _cacheOptions.LockTimeoutMilliseconds);

            _logger.LogInformation("初始化内存缓存管理器, 最大锁数量: {0}, 锁超时时间: {1}ms", _maxLocks, _lockTimeout.TotalMilliseconds);
        }

        #region ICacheManager

        /// <inheritdoc/>
        public bool IsLocalCache => true;

        /// <inheritdoc/>
        public Task SetAsync(string key, object? obj, CacheEntryOptions? cacheEntryOptions = null, CancellationToken token = default)
        {
            CheckDisposed();

            token.ThrowIfCancellationRequested();
            Set(key, obj, cacheEntryOptions);
            return Task.CompletedTask;
        }

        /// <inheritdoc/>
        public void Set(string key, object? obj, CacheEntryOptions? cacheEntryOptions = null)
        {
            CheckDisposed();
            if (string.IsNullOrWhiteSpace(key))
                return;

            var effective = PrepareEffectiveOptions(obj, cacheEntryOptions);
            var options = BuildMemoryCacheEntryOptions(effective);

            _memoryCache.Set(key, obj, options);
            OnAddKey(key);
        }

        /// <inheritdoc/>
        public (bool Success, TItem? Value) TryGetValue<TItem>(string key)
        {
            CheckDisposed();

            if (string.IsNullOrWhiteSpace(key))
                return (false, default);

            bool hit = _memoryCache.TryGetValue(key, out TItem? value);
            return (hit, value);
        }

        /// <inheritdoc/>
        public Task<(bool Success, TItem? Value)> TryGetValueAsync<TItem>(string key, CancellationToken token = default)
        {
            CheckDisposed();
            token.ThrowIfCancellationRequested();
            return Task.FromResult(TryGetValue<TItem>(key));
        }

        /// <inheritdoc/>
        public async Task<TData?> GetAsync<TData>(string key, Func<Task<TData?>> acquire, CacheEntryOptions? cacheEntryOptions = null, CancellationToken token = default)
        {
            CheckDisposed();
            Check.NotNull(acquire, nameof(acquire));

            if (string.IsNullOrWhiteSpace(key))
                return default;

            token.ThrowIfCancellationRequested();

            // 首先尝试从缓存获取
            if (_memoryCache.TryGetValue(key, out TData? cached))
                return cached; // 包含空值命中

            // 获取或创建该key对应的锁
            var lockRef = GetOrCreateLock(key);
            bool semaphoreAcquired = false;
            try
            {
                if (!await lockRef.Semaphore.WaitAsync(_lockTimeout, token).ConfigureAwait(false))
                    throw new TimeoutException($"获取缓存锁超时: {key}");
                semaphoreAcquired = true;

                if (_memoryCache.TryGetValue(key, out cached))
                    return cached;

                TData? data = await acquire().ConfigureAwait(false);

                var effective = PrepareEffectiveOptions(data, cacheEntryOptions);
                var entryOptions = BuildMemoryCacheEntryOptions(effective);
                _memoryCache.Set(key, data, entryOptions);
                OnAddKey(key);

                return data;
            }
            finally
            {
                if (semaphoreAcquired)
                    lockRef.Semaphore.Release();
                ReleaseLock(key, lockRef);
            }
        }

        /// <inheritdoc/>
        public Task<TData?> GetAsync<TData>(string key, Func<TData?> acquire, CacheEntryOptions? cacheEntryOptions = null, CancellationToken token = default)
        {
            // 将同步委托包装成异步委托
            if (acquire == null) throw new ArgumentNullException(nameof(acquire));
            Task<TData?> Wrapper() => Task.FromResult(acquire());
            return GetAsync(key, Wrapper, cacheEntryOptions, token);
        }

        /// <inheritdoc/>
        public TData? Get<TData>(string key, Func<TData?> acquire, CacheEntryOptions? cacheEntryOptions = null)
        {
            CheckDisposed();
            Check.NotNull(acquire, nameof(acquire));

            if (string.IsNullOrWhiteSpace(key))
                return default;

            // 首先尝试从缓存获取，包括空值结果
            if (_memoryCache.TryGetValue(key, out TData? cached))
                return cached;

            // 获取或创建该key对应的锁
            var lockRef = GetOrCreateLock(key);
            bool semaphoreAcquired = false;
            try
            {
                if (!lockRef.Semaphore.Wait(_lockTimeout))
                    throw new TimeoutException($"获取缓存锁超时: {key}");
                semaphoreAcquired = true;

                if (_memoryCache.TryGetValue(key, out cached))
                    return cached;

                TData? data = acquire();

                var effective = PrepareEffectiveOptions(data, cacheEntryOptions);
                var entryOptions = BuildMemoryCacheEntryOptions(effective);
                _memoryCache.Set(key, data, entryOptions);
                OnAddKey(key);

                return data;
            }
            finally
            {
                if (semaphoreAcquired)
                    lockRef.Semaphore.Release();
                ReleaseLock(key, lockRef);
            }
        }

        /// <inheritdoc/>
        public Task RemoveAsync(string key, CancellationToken token = default)
        {
            CheckDisposed();
            token.ThrowIfCancellationRequested();
            Remove(key);
            return Task.CompletedTask;
        }

        public Task RemoveAsync(IEnumerable<string> keys, CancellationToken token = default)
        {
            Check.NotNull(keys, nameof(keys));
            CheckDisposed();
            token.ThrowIfCancellationRequested();

            foreach (var k in keys)
            {
                token.ThrowIfCancellationRequested();
                Remove(k);
            }
            return Task.CompletedTask;
        }

        /// <inheritdoc/>
        public void Remove(string key)
        {
            CheckDisposed();
            if (string.IsNullOrWhiteSpace(key))
                return;

            _memoryCache.Remove(key);
            OnRemoveKey(key);
            CleanupLock(key);
        }

        public void Remove(IEnumerable<string> keys)
        {
            Check.NotNull(keys, nameof(keys));
            CheckDisposed();

            foreach (var key in keys)
            {
                Remove(key);
            }
        }

        /// <inheritdoc/>
        public Task RemoveByPrefixAsync(string prefix, CancellationToken token = default)
        {
            CheckDisposed();
            token.ThrowIfCancellationRequested();
            RemoveByPrefix(prefix);
            return Task.CompletedTask;
        }

        /// <inheritdoc/>
        public void RemoveByPrefix(string prefix)
        {
            CheckDisposed();
            if (string.IsNullOrWhiteSpace(prefix))
                return;

            foreach (var key in _keysList.Keys.ToList())
            {
                if (key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
                {
                    _memoryCache.Remove(key);
                    _keysList.TryRemove(key, out _);
                    CleanupLock(key);
                }
            }
        }

        /// <inheritdoc/>
        public Task ClearAsync(CancellationToken token = default)
        {
            CheckDisposed();
            token.ThrowIfCancellationRequested();
            Clear();
            return Task.CompletedTask;
        }

        /// <inheritdoc/>
        public void Clear()
        {
            CheckDisposed();
            var oldToken = Interlocked.Exchange(ref _clearToken, new CancellationTokenSource());
            oldToken.Cancel();
            oldToken.Dispose();

            _keysList.Clear();

            foreach (var pair in _locks.ToList())
            {
                CleanupLock(pair.Key);
            }
        }

        /// <inheritdoc/>
        public CacheEntryOptions CreateCacheEntryOptions() => _cacheOptions.CreateCacheEntryOptions();

        #endregion ICacheManager

        private CacheEntryOptions PrepareEffectiveOptions(object? data, CacheEntryOptions? provided)
        {
            // 空值使用专用策略
            if (data == null)
                return _cacheOptions.CreateNullValueCacheEntryOptions();

            return provided ?? _cacheOptions.CreateCacheEntryOptions();
        }

        /// <summary>
        /// 准备内存缓存项选项
        /// </summary>
        /// <param name="cacheEntryOptions">缓存项配置选项</param>
        /// <returns>内存缓存项选项</returns>
        private MemoryCacheEntryOptions BuildMemoryCacheEntryOptions(CacheEntryOptions cacheEntryOptions)
        {
            var options = new MemoryCacheEntryOptions
            {
                SlidingExpiration = cacheEntryOptions.SlidingExpiration,
                AbsoluteExpirationRelativeToNow = cacheEntryOptions.AbsoluteExpirationRelativeToNow,
                AbsoluteExpiration = cacheEntryOptions.AbsoluteExpiration
            };

            // 清理时通知
            options.AddExpirationToken(new CancellationChangeToken(_clearToken.Token));

            // 添加驱逐回调，自动维护 key 与锁
            options.RegisterPostEvictionCallback((key, value, reason, state) =>
            {
                if (key is string skey)
                {
                    OnRemoveKey(skey);
                    CleanupLock(skey);
                    _logger.LogDebug("缓存键驱逐: {Key}, 原因: {Reason}", skey, reason);
                }
            });

            return options;
        }

        /// <summary>
        /// 添加缓存键时触发
        /// </summary>
        /// <param name="key">缓存键</param>
        protected virtual void OnAddKey(string key) => _keysList.TryAdd(key, default);

        /// <summary>
        /// 移除缓存键时触发
        /// </summary>
        /// <param name="key">缓存键</param>
        protected virtual void OnRemoveKey(string key) => _keysList.TryRemove(key, out _);

        /// <inheritdoc/>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        /// <param name="disposing">是否释放托管资源</param>
        protected virtual void Dispose(bool disposing)
        {
            if (_disposed)
                return;

            if (disposing)
            {
                foreach (var pair in _locks)
                {
                    pair.Value.Dispose();
                }
                _locks.Clear();

                //_memoryCache.Dispose();
                _clearToken.Dispose();
            }

            _disposed = true;
        }

        private void CheckDisposed() => ObjectDisposedException.ThrowIf(_disposed, this);

        #region 信号量管理

        private class SemaphoreRef
        {
            public SemaphoreSlim Semaphore { get; }
            private int _refCount;
            private readonly object _syncLock = new object();

            public SemaphoreRef()
            {
                Semaphore = new SemaphoreSlim(1, 1);
            }

            public int AddRef()
            {
                return Interlocked.Increment(ref _refCount);
            }

            public int Release()
            {
                int newCount = Interlocked.Decrement(ref _refCount);
                if (newCount < 0)
                {
                    // 如果引用计数小于0，说明出现了错误，立即恢复
                    Interlocked.Increment(ref _refCount);
                    throw new InvalidOperationException("引用计数不能为负数");
                }
                return newCount;
            }

            public int RefCount
            {
                get => Volatile.Read(ref _refCount);
            }

            // 添加安全检查方法
            public bool IsDisposed { get; private set; }

            public void Dispose()
            {
                lock (_syncLock)
                {
                    if (IsDisposed) return;
                    Semaphore.Dispose();
                    IsDisposed = true;
                }
            }
        }

        private void CleanupExpiredLocks()
        {
            int cleaned = 0;
            foreach (var pair in _locks.ToList())
            {
                if (pair.Value.RefCount == 0)
                {
                    if (_locks.TryRemove(pair.Key, out var refObj))
                    {
                        if (!refObj.IsDisposed)
                            refObj.Dispose();
                        cleaned++;
                    }
                }
                if (_locks.Count < _maxLocks) break;
            }

            if (cleaned > 0)
                _logger.LogDebug("锁数量达到上限，清理无引用锁数量: {Count}", cleaned);
        }

        private SemaphoreRef GetOrCreateLock(string key)
        {
            // 达到上限尝试清理无引用锁
            if (_locks.Count >= _maxLocks)
            {
                CleanupExpiredLocks();
                if (_locks.Count >= _maxLocks)
                    throw new InvalidOperationException($"已达到最大锁数量限制({_maxLocks})，请稍后重试");
            }

            return _locks.AddOrUpdate(
                key,
                _ =>
                {
                    var created = new SemaphoreRef();
                    created.AddRef();
                    return created;
                },
                (_, existing) =>
                {
                    existing.AddRef();
                    return existing;
                });
        }

        private void ReleaseLock(string key, SemaphoreRef lockRef)
        {
            // 先递减引用计数
            int newCount = lockRef.Release();
            if (newCount == 0)
            {
                CleanupLock(key);
            }
        }

        // 添加一个辅助方法来处理锁的清理
        private void CleanupLock(string key)
        {
            // 使用原子操作尝试移除锁
            if (_locks.TryRemove(key, out var lockRef))
            {
                try
                {
                    // 再次检查引用计数，确保在移除过程中没有新的引用产生
                    if (lockRef.RefCount == 0 && !lockRef.IsDisposed)
                        lockRef.Dispose();
                    else if (lockRef.RefCount > 0 && !lockRef.IsDisposed)
                        _locks.TryAdd(key, lockRef);// 如果锁还在使用中，需要将其重新添加到字典中
                }
                catch (Exception ex)
                {
                    // 这里可以添加日志记录
                    _logger.LogError(ex, "清理键 {Key} 的锁时发生错误", key);

                    // 确保锁被重新添加到字典中
                    if (!lockRef.IsDisposed)
                    {
                        _locks.TryAdd(key, lockRef);
                    }
                }
            }
        }

        #endregion 信号量管理
    }
}